import requests
import time
import random
from multiprocessing import Process, Pool
# threading 是封装了 _Thread模块，API更丰富
from threading import Thread

"""
进程和线程在 windows
"""

result_list = []


def clock_it_deco(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print("EXECUTE TIME: {:.3f}s".format(end_time - start_time))
        return result
    # 返回函数名，不是函数调用，刚踩坑~
    return wrapper


# 请求淘宝网页
def taobao_request(index):
    # 请求网址
    url = 'https://s.taobao.com/search?'
    # 请求体 get
    request_body = "q=%E5%A5%B3%E8%A3%85&imgfile=&commend=all&ssid=s5-e&search_type=item&sourceId=tb.index&spm=a21bo.2017" \
             ".201856-taobao-item.1&ie=utf8&initiative_id=tbindexz_20170306 "
    # 请求头，不然会识别为机器爬虫
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                      'Chrome/85.0.4183.102 Safari/537.36 '
    }
    res = requests.get('{}{}'.format(url, request_body), headers=headers)
    random_time = random.random()*3
    time.sleep(random_time)
    print('random_time:{},--{} request res:{}'.format(random_time, index, res.status_code))
    result_list.append(res.status_code)
    return res.status_code


@clock_it_deco
def my_thread():
    """
    利用Python实现一个多线程程序
    打印时间
    在程序中调用time.sleep
    """
    t_list = []
    for index in range(10):
        # target： 要执行的函数，注意：这里是传入的函数名，不是函数的调用   args： 执行函数的参数，元组类型
        # 返回一个线程对象，还未执行，需调用start() 函数开启
        t = Thread(target=taobao_request, args=(index, ))
        # 开启线程，会开启一个子线程
        t.start()
        # t返回的是 <Thread(Thread-1, started 9188)> ... <Thread(Thread-10, initial)>
        # 会先打印，因为 start 后，会重新开启一个线程，不会阻塞下面的执行
        print(t)
        # 将线程实例添加到列表，后面一次性遍历
        t_list.append(t)

    # join 将子线程加入到当前线程中，这个方法可以实现多个线程执行完之后，再执行下面的代码
    [t.join() for t in t_list]
    print("done")

    # 多线程可以共享同一个内存资源，可以访问主进程的全局变量
    print(f'my_thread()访问全局变量result_list的结果为：{result_list}')
    # 清空result_list，不影响下面的进程测试
    result_list.clear()
    return None


@clock_it_deco
def my_process():
    """
    将上述多线程程序改为多进程
    打印时间
    """
    p_list = []
    for index in range(10):
        # target： 要执行的函数，注意：这里是传入的函数名，不是函数的调用   args： 执行函数的参数，元组类型
        # 返回一个进程对象，还未执行，需调用start() 函数开启
        p = Process(target=taobao_request, args=(index, ))
        # 开启进程
        p.start()
        # p返回的是 <Process name='Process-1' pid=10944 parent=11128 started>
        # 会先打印，因为 start 后，会开启一个子进程，跟主进程没有关系了，不会阻塞下面的执行
        print(f'进程实例对象p为：{p}')
        # 将进程实例添加到列表
        p_list.append(p)

    # join 将子进程加入到当前进程中，这个方法可以实现多个子进程执行完之后，再执行下面的代码，实现最后打印done
    [p.join() for p in p_list]
    print("done")

    # 子进程之间不共享作用域，不可以访问主进程的全局变量
    print(f'my_process()访问全局变量result_list的结果为：{result_list}')


@clock_it_deco
def my_pool():
    """
    进程池
    """
    pool = Pool(4)
    f_list = []
    for index in range(10):
        # 返回的是一个期物，可以获取到执行函数的返回值
        future = pool.apply_async(taobao_request, args=(index, ))
        print(f'future:{future}')
        f_list.append(future)
    # 关闭线程池
    pool.close()
    # 将子进程加入到当前进程中
    pool.join()
    # f.get()获取函数执行的结果
    [print(f.get()) for f in f_list]


if __name__ == '__main__':
    my_thread()
    my_process()
    my_pool()
